#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/timer.h>

#define KEY_IOCTL_BASE 'x'
#define KEY_IOCTL_UP    _IOW(KEY_IOCTL_BASE,  0, unsigned long) 
#define KEY_IOCTL_DOWN  _IOW(KEY_IOCTL_BASE,  1, unsigned long) 
#define KEY_IOCTL_LEFT  _IOW(KEY_IOCTL_BASE,  2, unsigned long) 
#define KEY_IOCTL_RIGHT _IOW(KEY_IOCTL_BASE,  3, unsigned long) 
#define KEY_IOCTL_A 	_IOW(KEY_IOCTL_BASE,  4, unsigned long) 
#define KEY_IOCTL_S 	_IOW(KEY_IOCTL_BASE,  5, unsigned long) 
#define KEY_IOCTL_Z 	_IOW(KEY_IOCTL_BASE,  6, unsigned long) 
#define KEY_IOCTL_X		_IOW(KEY_IOCTL_BASE,  7, unsigned long) 
#define KEY_IOCTL_ENTER	_IOW(KEY_IOCTL_BASE,  8, unsigned long) 
#define KEY_IOCTL_ESC	_IOW(KEY_IOCTL_BASE,  9, unsigned long) 
#define KEY_IOCTL_SUPER	_IOW(KEY_IOCTL_BASE, 10, unsigned long) 


static struct input_dev *a8_keys;
static struct timer_list a8_timer;

static char key_name[8][20] = {
	"eint16_key2", "eint17_key3", "eint18_key4", "eint19_key5",
	"eint24_key6", "eint25_key7", "eint26_key8", "eint27_key9"
};

static long key_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	arg = arg ? 1 : 0;

	printk("cmd = %u, arg = %lu\n", cmd, arg);

	switch (cmd) {
		case KEY_IOCTL_UP: {
			input_report_key(a8_keys, KEY_UP, arg);
			break;
		}
		case KEY_IOCTL_DOWN: {
			input_report_key(a8_keys, KEY_DOWN, arg);
			break;
		}
		case KEY_IOCTL_LEFT: {
			input_report_key(a8_keys, KEY_LEFT, arg);
			break;
		}
		case KEY_IOCTL_RIGHT: {
			input_report_key(a8_keys, KEY_RIGHT, arg);
			break;
		}
		case KEY_IOCTL_A: {
			input_report_key(a8_keys, KEY_A, arg);
			break;
		}
		case KEY_IOCTL_S: {
			input_report_key(a8_keys, KEY_S, arg);
			break;
		}
		case KEY_IOCTL_Z: {
			input_report_key(a8_keys, KEY_Z, arg);
			break;
		}
		case KEY_IOCTL_X: {
			input_report_key(a8_keys, KEY_X, arg);
			break;
		}
		case KEY_IOCTL_ENTER: {
			input_report_key(a8_keys, KEY_ENTER, arg);
			break;
		}
		case KEY_IOCTL_ESC: {
			input_report_key(a8_keys, KEY_ESC, arg);
			break;
		}
		case KEY_IOCTL_SUPER: {
			mod_timer(&a8_timer, jiffies + 5);
			break;
		}
		default:
			return -EFAULT;
	}
	return 0;
}

static irqreturn_t keyn_irq(int irq, void *dev_id)
{
	//添加中断处理的代码
	int i;
	int key_value=0;	
	for (i=0; i<4; i++) {
		if (irq == IRQ_EINT(16+i))
			key_value = gpio_get_value(S5PV210_GPH2(i));
		if (irq == IRQ_EINT(24+i))	
			key_value = gpio_get_value(S5PV210_GPH3(i));			
	}
	
	if (irq == IRQ_EINT(16))
		input_report_key(a8_keys, KEY_ENTER, !key_value);		
	if (irq == IRQ_EINT(17))
		input_report_key(a8_keys, KEY_Z, !key_value);		
	if (irq == IRQ_EINT(18))
		input_report_key(a8_keys, KEY_X, !key_value);
	if (irq == IRQ_EINT(19))
		input_report_key(a8_keys, KEY_UP, !key_value);
	
	if (irq == IRQ_EINT(24))
		input_report_key(a8_keys, KEY_DOWN, !key_value);		
	if (irq == IRQ_EINT(25))
		input_report_key(a8_keys, KEY_LEFT, !key_value);		
	if (irq == IRQ_EINT(26))
		input_report_key(a8_keys, KEY_RIGHT, !key_value);

	if (irq == IRQ_EINT(27) && key_value)
		mod_timer(&a8_timer, jiffies + 5);
	//报告结束
	input_sync(a8_keys);
    return IRQ_HANDLED;//中断处理结束
}


static void a8_timer_fun(unsigned long data)
{
	static int cnt=0;	
	if (cnt == 0)
        input_report_key(a8_keys, KEY_DOWN, 1);    
    if (cnt == 1)
        input_report_key(a8_keys, KEY_RIGHT, 1);    
    if (cnt == 2)
        input_report_key(a8_keys, KEY_DOWN, 0);          
    if (cnt == 3) {
        input_report_key(a8_keys, KEY_RIGHT, 0);
        input_report_key(a8_keys, KEY_DOWN, 1);
    }
    if (cnt == 4)
        input_report_key(a8_keys, KEY_RIGHT, 1);
    if (cnt == 5) {
        input_report_key(a8_keys, KEY_DOWN, 0); 
        input_report_key(a8_keys, KEY_X, 1);
    }        
    if (cnt == 6) {
        input_report_key(a8_keys, KEY_RIGHT, 0);
        input_report_key(a8_keys, KEY_X, 0);
    }
    cnt++;
    if (cnt < 7)
        mod_timer(&a8_timer, jiffies + 5);
    if (cnt == 7)
        cnt =0;
}

static struct file_operations key_fops = {
	.owner = THIS_MODULE,
	.unlocked_ioctl	= key_ioctl
};

static struct miscdevice key_misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.name  = "key_drv",
	.fops  = &key_fops,
};

static int __init s5pv_key_init(void)
{
	int rt;
	int i;	
	//申请中断
	for (i=0; i<4; i++) {
		rt=request_irq(IRQ_EINT(16+i), keyn_irq,
				IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,key_name[i],NULL);

		if (rt) {
			for (i--; i >= 0; i--)
				free_irq(IRQ_EINT(16+i),NULL);	
		}
	}

	for (i=0; i<4; i++) {
		rt=request_irq(IRQ_EINT(24+i), keyn_irq,
			IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,key_name[i+4],NULL);

		if(rt) {
			for (i--; i >= 0; i--)
				free_irq(IRQ_EINT(24+i),NULL);	
			goto fail_key6_9_request_irq;
		}
	}
	
	//申请GPIO
	for (i=0; i<4; i++) {
		rt = gpio_request(S5PV210_GPH2(i),"GPH2");
		if(rt) {
			for (i--; i >= 0; i--)
				gpio_free(S5PV210_GPH2(i));
			goto fail_gpio_request_gph2;
		}
	}	
	
	for (i=0; i<4; i++) {
		rt = gpio_request(S5PV210_GPH3(i),"GPH3");
		if(rt) {
			for (i--; i >= 0; i--)
				gpio_free(S5PV210_GPH3(i));
			goto fail_gpio_request_gph3;
		}			
	}		
	
	//输入设备分配空间
	a8_keys=input_allocate_device();	
	if (a8_keys == NULL) {
		rt = -ENOMEM;
		goto fail_input_allocate_device;
	}
	
	//初始化基本信息
	a8_keys->name = "a8_keys";
	a8_keys->id.bustype = 0x0000;
	a8_keys->id.vendor  = 0x0001;
	a8_keys->id.product = 0x0002;
	a8_keys->id.version = 0x1001;	
	
	//初始化设备类型为EV_KEY
	set_bit(EV_KEY,a8_keys->evbit);	
	//初始化输入设备的编码
    set_bit(KEY_Z,     a8_keys->keybit);
    set_bit(KEY_X,     a8_keys->keybit); 
    set_bit(KEY_A,     a8_keys->keybit);
    set_bit(KEY_S,     a8_keys->keybit);
    // set_bit(KEY_W,     a8_keys->keybit);
    set_bit(KEY_LEFT,  a8_keys->keybit);
    set_bit(KEY_RIGHT, a8_keys->keybit);
    set_bit(KEY_UP,    a8_keys->keybit);    
    set_bit(KEY_DOWN,  a8_keys->keybit);
    set_bit(KEY_ESC,   a8_keys->keybit);
    set_bit(KEY_ENTER, a8_keys->keybit); 	
	
	//将输入设备注册到内核
	rt = input_register_device(a8_keys);	
	if (rt)
		goto fail_input_register_device;

	init_timer(&a8_timer);
	a8_timer.function = a8_timer_fun;
	a8_timer.expires = jiffies + 5;

	rt = misc_register(&key_misc);
	if (rt)	
		goto fail_misc_register;
	

	printk("linux key device irq driver ok\n");
	return 0;

fail_misc_register:
	del_timer(&a8_timer);
fail_input_register_device:
	input_free_device(a8_keys);	
fail_input_allocate_device:
	for (i=0; i<4; i++)
		gpio_free(S5PV210_GPH3(i));	
fail_gpio_request_gph3:	
	for (i=0; i<4; i++)
		gpio_free(S5PV210_GPH2(i));	
fail_gpio_request_gph2:	
	for (i=0; i<4; i++)
		free_irq(IRQ_EINT(24+i),NULL);
fail_key6_9_request_irq:
	for (i=0; i<4; i++)
		free_irq(IRQ_EINT(16+i),NULL);

	return rt;
}


static void __exit s5pv_key_exit(void)
{
	int i;
	
	misc_deregister(&key_misc);
	input_unregister_device(a8_keys);	
	input_free_device(a8_keys);	
	del_timer(&a8_timer);
	
	for (i=0; i<4; i++)
		gpio_free(S5PV210_GPH3(i));		
	for (i=0; i<4; i++)
		gpio_free(S5PV210_GPH2(i));		
	for (i=0; i<4; i++) {
		free_irq(IRQ_EINT(16+i),NULL);
		free_irq(IRQ_EINT(24+i),NULL);
	}
	printk("key device driver unregister\n");
	
}

module_init(s5pv_key_init);
module_exit(s5pv_key_exit);

MODULE_AUTHOR("Jacksonlike");
MODULE_DESCRIPTION("S5PV210 Key Input Device driver");
MODULE_LICENSE("GPL");	